package factory;

import java.util.ArrayList;
import java.util.List;

import factory.Machine;
import factory.Job;
import base.Item;
import base.Parameter;
import base.ReferenceParameter;
import gene.Gene;
import util.Log;

public class Schedule extends Item implements Machine.OnCompleteListenner {

	public Schedule() {
		setName("Schedule");
	}

	private int end = 0;

	private List<Double> resultCaculation = new ArrayList<Double>();

	private double cmax = 0;

	private double fitness;
	
	private double fitnessCompare;

	private long currentTime;

	private List<Machine> machineIndex = new ArrayList<Machine>();
	protected List<Machine> checkIndex = new ArrayList<Machine>();

	List<ReferenceParameter> rpList;

	int index;

	private ArrayList<Job> jobList;

	private OnCompleteListenner onCompleteListenner;

	public void setOnCompleteListenner(OnCompleteListenner onCompleteListenner) {
		this.onCompleteListenner = onCompleteListenner;
	}

	private Gene gene;

	public void setGene(Gene gene) {
		this.gene = gene;
	}

	public Gene getGene() {
		return gene;
	}

	public Job getNextJob() {

		if (isAvailable()) {
			double minValue = Double.MAX_VALUE;
			int minIndex = -1;

			//
			for (int i = 0; i < jobList.size(); i++) {
				for (int j = 0; j < machineIndex.size(); j++) {
					if (!machineIndex.get(j).isBusy()) {
						gene.getRootNode().setJob(rpList.get(index).getJob(jobList.get(i).getJobIndex()));
						gene.getRootNode().setSchedule(this, currentTime);
						gene.getRootNode().setMachine(machineIndex.get(j));
						// Log.debug("================================================다음잡 시작 "
						// + jobList.get(i).getJobIndex());
						// Log.debug("================================================다음잡 시간 "
						// + jobList.get(i).getProcessingTime());
						// Log.debug("================================================계산에 참여된 머신  시작 "
						// + machineIndex.get(j).getIndex());
						// Log.debug("================================================계산에 참여된 머신  레디타임 "
						// + machineIndex.get(j).getEndTime());
						// Log.debug("================================================고려된 시간 "+
						// currentTime);

						double temp = 0;
						temp = gene.calculateGene();
						// Log.debug("================================================연산 값 "
						// + temp);
						// Log.debug("================================================연산한번끝 ");
						if (minValue > temp) {
							minValue = temp;
							minIndex = i;
						}
					}
				}
			}
			// Log.debug("민벨류 " + minValue);

			// Log.debug("선택잡 index" + minIndex);
			// Log.debug("선택잡 index" + jobList.get(minIndex).getJobIndex());
			// Log.debug("선택잡 pt" + jobList.get(minIndex).getProcessingTime());

			Job returnJob = jobList.get(minIndex);

			jobList.remove(returnJob);

			return returnJob;
		} else {
			return null;
		}
	}

	public void run() {

		while (jobList.size() > 0) {
			// Log.debug("================================================잡 갯수 "
			// + jobList.size());
			for (int i = 0; i < machineIndex.size(); i++) {
				if (!machineIndex.get(i).isBusy()) {
					Job temp = getNextJob();

					if (temp != null) {
						machineIndex.get(i).run(temp);
						// Log.debug("================================================잡 할당 "
						// + temp.getJobIndex());
					}
				}
			}
			currentTime++;

			checkMachineFree(currentTime);
			// Log.debug("================================================첫번째 현재 시간 "
			// + currentTime);
		}

		for (int i = 0; i < machineIndex.size(); i++) {
			if (machineIndex.get(i).isBusy()) { // idle
				while (machineIndex.get(i).isBusy()) {
					currentTime++;
					checkMachineFree(currentTime);
				}
			}
		}
		end = -1;
	}

	public void checkMachineFree(long currentTime) {
		for (int j = 0; j < machineIndex.size(); j++) {
			if (currentTime == machineIndex.get(j).getEndTime()) {
				machineIndex.get(j).onComplete();
			} else {
				// Log.debug("FUCK");
			}
		}
	}

	public boolean isAvailable() {

		boolean machineCheck = false;

		for (int i = 0; i < machineIndex.size(); i++) {
			if (machineIndex.get(i).isBusy() == false) {
				machineCheck = true;
			}
		}

		if (jobList.size() > 0 && machineCheck == true) {

			// Log.debug(" machineCheck " + machineCheck + " jobsize " +
			// jobList.size());

			return true;
			// when true, go to getNextJob
		} else {
			return false;
		}
	}

	public ArrayList<Job> getJobList() {
		return jobList;
	}

	public void setJobList(ArrayList<Job> jobList) {
		this.jobList = jobList;
	}

	public void scheduleParameter(int index, Parameter parameters, List<ReferenceParameter> rpList, Gene gene) {

		setGene(gene);
		this.index = index;
		this.rpList = rpList;
		this.jobList = new ArrayList<Job>(rpList.get(index).getReferenceJobList());

		for (int i = 0; i < jobList.size(); i++) {
			jobList.get(i).setJobIndex(i);
		}

		for (int i = 0; i < rpList.get(index).getRereferenceMachineNumber(); i++) {
			Machine machine = new Machine();
			machine.setIndex(i);
			machine.setOnCompleteListenner(this);
			machineIndex.add(machine);
		}
	}

	@Override
	public void onComplete(Machine machine) {

		if (onCompleteListenner == null)
			return;
		onCompleteListenner.onComplete(this); // 머신 매니저의 onComplete 호출

	}

	public interface OnCompleteListenner {

		public abstract void onComplete(Schedule schedule);

	}

	public int getIndex() {
		return index;
	}

	public long getCurrentTime() {
		return currentTime;
	}

	public void setCurrentTime(long currentTime) {
		this.currentTime = currentTime;
	}

	public List<Machine> getMachineIndex() {
		return machineIndex;
	}

	public double getCmax() {

		double min = 0;

		for (int i = 0; i < machineIndex.size(); i++) {
			if (min < machineIndex.get(i).getTotalWorkingTime()) {
				cmax = machineIndex.get(i).getTotalWorkingTime();
				min = machineIndex.get(i).getTotalWorkingTime();
			}
		}
		return cmax;
	}
	
	public double getFitness(){

		double temp = getCmax();

		return fitness=temp;
	}
	
	
	public double getFitnessCompare() {

		double temp = getCmax();

	//	Log.debug(" cmax " + temp);
		
	//	Log.debug(" rcmax " + rpList.get(index).getReferenceCmax());

		fitnessCompare = temp - rpList.get(index).getReferenceCmax();
		
	//	Log.debug(" fitness " + fitness);

		return fitnessCompare;
	}

	public int getEnd() {
		return end;
	}

	public List<Double> getResultCaculation() {
		return resultCaculation;
	}

	public List<ReferenceParameter> getRpListInSchedule() {
		return rpList;
	}

}
